home *** CD-ROM | disk | FTP | other *** search
/ CU Amiga Super CD-ROM 16 / CU Amiga Magazine's Super CD-ROM 16 (1997-10-16)(EMAP Images)(GB)[!][issue 1997-11].iso / CUCD / Graphics / Ghostscript / source / gsalloc.c < prev    next >
C/C++ Source or Header  |  1997-06-20  |  39KB  |  1,270 lines

  1. /* Copyright (C) 1995, 1996, 1997 Aladdin Enterprises.  All rights reserved.
  2.   
  3.   This file is part of Aladdin Ghostscript.
  4.   
  5.   Aladdin Ghostscript is distributed with NO WARRANTY OF ANY KIND.  No author
  6.   or distributor accepts any responsibility for the consequences of using it,
  7.   or for whether it serves any particular purpose or works at all, unless he
  8.   or she says so in writing.  Refer to the Aladdin Ghostscript Free Public
  9.   License (the "License") for full details.
  10.   
  11.   Every copy of Aladdin Ghostscript must include a copy of the License,
  12.   normally in a plain ASCII text file named PUBLIC.  The License grants you
  13.   the right to copy, modify and redistribute Aladdin Ghostscript, but only
  14.   under certain conditions described in the License.  Among other things, the
  15.   License requires that the copyright notice and this notice be preserved on
  16.   all copies.
  17. */
  18.  
  19. /* gsalloc.c */
  20. /* Standard memory allocator */
  21. #include "gx.h"
  22. #include "memory_.h"
  23. #include "gsmdebug.h"
  24. #include "gsstruct.h"
  25. #include "gxalloc.h"
  26.  
  27. /*
  28.  * This allocator produces tracing messages of the form
  29.  *    [aNMOTS]...
  30.  * where
  31.  *   N is the VM space number,
  32.  *   M is : for movable objects, | for immovable,
  33.  *   O is {alloc = +, free = -, grow = >, shrink = <},
  34.  *   T is {bytes = b, object = <, ref = $, string = >}, and
  35.  *   S is {freelist = F, LIFO = space, own chunk = L, lost = #,
  36.  *    lost own chunk = ~, other = .}.
  37.  */
  38.  
  39. /* The structure descriptor for allocators.  Even though allocators */
  40. /* are allocated outside GC space, they reference objects within it. */
  41. public_st_ref_memory();
  42. #define mptr ((gs_ref_memory_t *)vptr)
  43. private ENUM_PTRS_BEGIN(ref_memory_enum_ptrs) return 0;
  44.     ENUM_PTR(0, gs_ref_memory_t, changes);
  45.     ENUM_PTR(1, gs_ref_memory_t, saved);
  46. ENUM_PTRS_END
  47. private RELOC_PTRS_BEGIN(ref_memory_reloc_ptrs) {
  48.     RELOC_PTR(gs_ref_memory_t, changes);
  49.     /* Don't relocate the pointer now -- see igc.c for details. */
  50.     mptr->reloc_saved = gs_reloc_struct_ptr(mptr->saved, gcst);
  51. } RELOC_PTRS_END
  52.  
  53. /* Forward references */
  54. private ulong compute_free_objects(P1(gs_ref_memory_t *));
  55. private obj_header_t *alloc_obj(P5(gs_ref_memory_t *, ulong, gs_memory_type_ptr_t, bool, client_name_t));
  56. private chunk_t *alloc_add_chunk(P4(gs_ref_memory_t *, ulong, bool, client_name_t));
  57. void alloc_close_chunk(P1(gs_ref_memory_t *));
  58.  
  59. #define imem ((gs_ref_memory_t *)mem)
  60.  
  61. /*
  62.  * Define the standard implementation (with garbage collection)
  63.  * of Ghostscript's memory manager interface.
  64.  */
  65. private gs_memory_proc_alloc_bytes(i_alloc_bytes);
  66. private gs_memory_proc_alloc_bytes(i_alloc_bytes_immovable);
  67. private gs_memory_proc_alloc_struct(i_alloc_struct);
  68. private gs_memory_proc_alloc_struct(i_alloc_struct_immovable);
  69. private gs_memory_proc_alloc_byte_array(i_alloc_byte_array);
  70. private gs_memory_proc_alloc_byte_array(i_alloc_byte_array_immovable);
  71. private gs_memory_proc_alloc_struct_array(i_alloc_struct_array);
  72. private gs_memory_proc_alloc_struct_array(i_alloc_struct_array_immovable);
  73. private gs_memory_proc_resize_object(i_resize_object);
  74. private gs_memory_proc_object_size(i_object_size);
  75. private gs_memory_proc_object_type(i_object_type);
  76. private gs_memory_proc_free_object(i_free_object);
  77. private gs_memory_proc_alloc_string(i_alloc_string);
  78. private gs_memory_proc_alloc_string(i_alloc_string_immovable);
  79. private gs_memory_proc_resize_string(i_resize_string);
  80. private gs_memory_proc_free_string(i_free_string);
  81. private gs_memory_proc_register_root(i_register_root);
  82. private gs_memory_proc_unregister_root(i_unregister_root);
  83. private gs_memory_proc_status(i_status);
  84. private gs_memory_proc_enable_free(i_enable_free);
  85. /* We export the procedures for subclasses. */
  86. const gs_memory_procs_t gs_ref_memory_procs = {
  87.     i_alloc_bytes,
  88.     i_alloc_bytes_immovable,
  89.     i_alloc_struct,
  90.     i_alloc_struct_immovable,
  91.     i_alloc_byte_array,
  92.     i_alloc_byte_array_immovable,
  93.     i_alloc_struct_array,
  94.     i_alloc_struct_array_immovable,
  95.     i_resize_object,
  96.     i_object_size,
  97.     i_object_type,
  98.     i_free_object,
  99.     i_alloc_string,
  100.     i_alloc_string_immovable,
  101.     i_resize_string,
  102.     i_free_string,
  103.     i_register_root,
  104.     i_unregister_root,
  105.     i_status,
  106.     i_enable_free
  107. };
  108. /*
  109.  * Allocate and mostly initialize the state of an allocator (system, global,
  110.  * or local).  Does not initialize global or space.
  111.  */
  112. private void *ialloc_solo(P3(gs_memory_t *, gs_memory_type_ptr_t, chunk_t **));
  113. gs_ref_memory_t *
  114. ialloc_alloc_state(gs_memory_t *parent, uint chunk_size)
  115. {    chunk_t *cp;
  116.     gs_ref_memory_t *iimem = ialloc_solo(parent, &st_ref_memory, &cp);
  117.  
  118.     if ( iimem == 0 )
  119.       return 0;
  120.     iimem->procs = gs_ref_memory_procs;
  121.     iimem->parent = parent;
  122.     iimem->chunk_size = chunk_size;
  123.     iimem->large_size = ((chunk_size / 4) & -obj_align_mod) + 1;
  124.     iimem->gc_status.vm_threshold = chunk_size * 3L;
  125.     iimem->gc_status.max_vm = max_long;
  126.     iimem->gc_status.psignal = NULL;
  127.     iimem->gc_status.enabled = false;
  128.     iimem->previous_status.allocated = 0;
  129.     iimem->previous_status.used = 0;
  130.     ialloc_reset(iimem);
  131.     iimem->cfirst = iimem->clast = cp;
  132.     ialloc_set_limit(iimem);
  133.     iimem->cc.cbot = iimem->cc.ctop = 0;
  134.     iimem->pcc = 0;
  135.     iimem->roots = 0;
  136.     iimem->num_contexts = 1;
  137.     iimem->saved = 0;
  138.     return iimem;
  139. }
  140. /* Allocate a 'solo' object with its own chunk. */
  141. private void *
  142. ialloc_solo(gs_memory_t *parent, gs_memory_type_ptr_t pstype, chunk_t **pcp)
  143. {    /*
  144.      * We can't assume that the parent uses the same object header
  145.      * that we do, but the GC requires that allocators have
  146.      * such a header.  Therefore, we prepend one explicitly.
  147.      */
  148.     chunk_t *cp = gs_alloc_struct_immovable(parent, chunk_t, &st_chunk,
  149.                         "ialloc_solo(chunk)");
  150.     uint csize =
  151.       round_up(sizeof(chunk_head_t) + sizeof(obj_header_t) +
  152.              pstype->ssize,
  153.            obj_align_mod);
  154.     byte *cdata = gs_alloc_bytes_immovable(parent, csize, "ialloc_solo");
  155.     obj_header_t *obj = (obj_header_t *)(cdata + sizeof(chunk_head_t));
  156.  
  157.     if ( cp == 0 || cdata == 0 )
  158.       return 0;
  159.     alloc_init_chunk(cp, cdata, cdata + csize, false, (chunk_t *)NULL);
  160.     cp->cbot = cp->ctop;
  161.     cp->cprev = cp->cnext = 0;
  162.     /* Construct the object header "by hand". */
  163.     obj->o_large = 0;
  164.     obj->o_size = pstype->ssize;
  165.     obj->o_type = pstype;
  166.     *pcp = cp;
  167.     return (void *)(obj + 1);
  168. }
  169. /* Initialize after a save. */
  170. void
  171. ialloc_reset(gs_ref_memory_t *mem)
  172. {    mem->cfirst = 0;
  173.     mem->clast = 0;
  174.     mem->cc.rcur = 0;
  175.     mem->cc.rtop = 0;
  176.     mem->cc.has_refs = false;
  177.     mem->allocated = 0;
  178.     mem->inherited = 0;
  179.     mem->changes = 0;
  180.     ialloc_reset_free(mem);
  181. }
  182. /* Initialize after a save or GC. */
  183. void
  184. ialloc_reset_free(gs_ref_memory_t *mem)
  185. {    int i;
  186.     obj_header_t **p;
  187.     mem->lost.objects = 0;
  188.     mem->lost.refs = 0;
  189.     mem->lost.strings = 0;
  190.     mem->cfreed.cp = 0;
  191.     for ( i = 0, p = &mem->freelists[0]; i < num_freelists; i++, p++ )
  192.       *p = 0;
  193. }
  194. /* Set the allocation limit after a change in one or more of */
  195. /* vm_threshold, max_vm, or enabled, or after a GC. */
  196. void
  197. ialloc_set_limit(register gs_ref_memory_t *mem)
  198. {    /*
  199.      * The following code is intended to set the limit so that
  200.      * we stop allocating when allocated + previous_status.allocated
  201.      * exceeds the lesser of max_vm or (if GC is enabled)
  202.      * gc_allocated + vm_threshold.
  203.      */
  204.     ulong max_allocated =
  205.       (mem->gc_status.max_vm > mem->previous_status.allocated ?
  206.        mem->gc_status.max_vm - mem->previous_status.allocated :
  207.        0);
  208.     if ( mem->gc_status.enabled )
  209.       {    ulong limit = mem->gc_allocated + mem->gc_status.vm_threshold;
  210.         if ( limit < mem->previous_status.allocated )
  211.           mem->limit = 0;
  212.         else
  213.           {    limit -= mem->previous_status.allocated;
  214.             mem->limit = min(limit, max_allocated);
  215.           }
  216.       }
  217.     else
  218.       mem->limit = max_allocated;
  219.     if_debug7('0', "[0]space=%d, max_vm=%ld, prev.alloc=%ld, enabled=%d,\n\
  220.       gc_alloc=%ld, threshold=%ld => limit=%ld\n",
  221.           mem->space, (long)mem->gc_status.max_vm,
  222.           (long)mem->previous_status.allocated,
  223.           mem->gc_status.enabled, (long)mem->gc_allocated,
  224.           (long)mem->gc_status.vm_threshold, (long)mem->limit);
  225. }
  226.  
  227. /* ================ Accessors ================ */
  228.  
  229. /* Get the size of an object from the header. */
  230. private uint
  231. i_object_size(gs_memory_t *mem, const void /*obj_header_t*/ *obj)
  232. {    return pre_obj_contents_size((const obj_header_t *)obj - 1);
  233. }
  234.  
  235. /* Get the type of a structure from the header. */
  236. private gs_memory_type_ptr_t
  237. i_object_type(gs_memory_t *mem, const void /*obj_header_t*/ *obj)
  238. {    return ((const obj_header_t *)obj - 1)->o_type;
  239. }
  240.  
  241. /* Get the GC status of a memory. */
  242. void
  243. gs_memory_gc_status(const gs_ref_memory_t *mem, gs_memory_gc_status_t *pstat)
  244. {    *pstat = mem->gc_status;
  245. }
  246.  
  247. /* Set the GC status of a memory. */
  248. void
  249. gs_memory_set_gc_status(gs_ref_memory_t *mem, const gs_memory_gc_status_t *pstat)
  250. {    mem->gc_status = *pstat;
  251.     ialloc_set_limit(mem);
  252. }
  253.  
  254. /* ================ Objects ================ */
  255.  
  256. /* Allocate a small object quickly if possible. */
  257. /* The size must be substantially less than max_uint. */
  258. /* ptr must be declared as obj_header_t *. */
  259. /* pfl must be declared as obj_header_t **. */
  260. #define IF_FREELIST_ALLOC(ptr, imem, size, pstype, pfl)\
  261.     if ( size <= max_freelist_size &&\
  262.          *(pfl = &imem->freelists[(size + obj_align_mask) >> log2_obj_align_mod]) != 0\
  263.        )\
  264.     {    ptr = *pfl;\
  265.         *pfl = *(obj_header_t **)ptr;\
  266.         ptr[-1].o_size = size;\
  267.         ptr[-1].o_type = pstype;\
  268.         /* If debugging, clear the block in an attempt to */\
  269.         /* track down uninitialized data errors. */\
  270.         gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
  271. #define ELSEIF_LIFO_ALLOC(ptr, imem, size, pstype)\
  272.     }\
  273.     else if ( (imem->cc.ctop - (byte *)(ptr = (obj_header_t *)imem->cc.cbot))\
  274.         >= size + (obj_align_mod + sizeof(obj_header_t) * 2) &&\
  275.          size < imem->large_size\
  276.        )\
  277.     {    imem->cc.cbot = (byte *)ptr + obj_size_round(size);\
  278.         ptr->o_large = 0;\
  279.         ptr->o_size = size;\
  280.         ptr->o_type = pstype;\
  281.         ptr++;\
  282.         /* If debugging, clear the block in an attempt to */\
  283.         /* track down uninitialized data errors. */\
  284.         gs_alloc_fill(ptr, gs_alloc_fill_alloc, size);
  285. #define ELSE_ALLOC\
  286.     }\
  287.     else
  288.  
  289. private byte *
  290. i_alloc_bytes(gs_memory_t *mem, uint size, client_name_t cname)
  291. {    obj_header_t *obj;
  292.     obj_header_t **pfl;
  293.     IF_FREELIST_ALLOC(obj, imem, size, &st_bytes, pfl)
  294.         if_debug4('A', "[a%d:+bF]%s -bytes-(%u) = 0x%lx\n",
  295.               imem->space,
  296.               client_name_string(cname), size, (ulong)obj);
  297.     ELSEIF_LIFO_ALLOC(obj, imem, size, &st_bytes)
  298.         if_debug4('A', "[a%d:+b ]%s -bytes-(%u) = 0x%lx\n",
  299.               imem->space,
  300.               client_name_string(cname), size, (ulong)obj);
  301.     ELSE_ALLOC
  302.     {    obj = alloc_obj(imem, size, &st_bytes, false, cname);
  303.         if ( obj == 0 )
  304.           return 0;
  305.         if_debug4('A', "[a%d:+b.]%s -bytes-(%u) = 0x%lx\n",
  306.               imem->space,
  307.               client_name_string(cname), size, (ulong)obj);
  308.     }
  309.     return (byte *)obj;
  310. }
  311. private byte *
  312. i_alloc_bytes_immovable(gs_memory_t *mem, uint size, client_name_t cname)
  313. {    obj_header_t *obj = alloc_obj(imem, size, &st_bytes, true, cname);
  314.     if ( obj == 0 )
  315.       return 0;
  316.     if_debug4('A', "[a%d|+b.]%s -bytes-(%u) = 0x%lx\n",
  317.           imem->space, client_name_string(cname), size, (ulong)obj);
  318.     return (byte *)obj;
  319. }
  320. private void *
  321. i_alloc_struct(gs_memory_t *mem, gs_memory_type_ptr_t pstype,
  322.   client_name_t cname)
  323. {    uint size = pstype->ssize;
  324.     obj_header_t *obj;
  325.     obj_header_t **pfl;
  326.     IF_FREELIST_ALLOC(obj, imem, size, pstype, pfl)
  327.         if_debug5('A', "[a%d:+<F]%s %s(%u) = 0x%lx\n",
  328.               imem->space, client_name_string(cname),
  329.               struct_type_name_string(pstype), size, (ulong)obj);
  330.     ELSEIF_LIFO_ALLOC(obj, imem, size, pstype)
  331.         if_debug5('A', "[a%d:+< ]%s %s(%u) = 0x%lx\n",
  332.               imem->space, client_name_string(cname),
  333.               struct_type_name_string(pstype), size, (ulong)obj);
  334.     ELSE_ALLOC
  335.     {    obj = alloc_obj(imem, size, pstype, false, cname);
  336.         if ( obj == 0 )
  337.           return 0;
  338.         if_debug5('A', "[a%d:+<.]%s %s(%u) = 0x%lx\n",
  339.               imem->space, client_name_string(cname),
  340.               struct_type_name_string(pstype), size, (ulong)obj);
  341.     }
  342.     return obj;
  343. }
  344. private void *
  345. i_alloc_struct_immovable(gs_memory_t *mem, gs_memory_type_ptr_t pstype,
  346.   client_name_t cname)
  347. {    uint size = pstype->ssize;
  348.     obj_header_t *obj = alloc_obj(imem, size, pstype, true, cname);
  349.     if ( obj == 0 )
  350.       return 0;
  351.     if_debug5('A', "[a%d|+<.]%s %s(%u) = 0x%lx\n",
  352.           imem->space, client_name_string(cname),
  353.           struct_type_name_string(pstype), size, (ulong)obj);
  354.     return obj;
  355. }
  356. private byte *
  357. i_alloc_byte_array(gs_memory_t *mem, uint num_elements, uint elt_size,
  358.   client_name_t cname)
  359. {    obj_header_t *obj = alloc_obj(imem, (ulong)num_elements * elt_size,
  360.                       &st_bytes, false, cname);
  361.     if_debug6('A', "[a%d:+b.]%s -bytes-*(%lu=%u*%u) = 0x%lx\n",
  362.           imem->space, client_name_string(cname),
  363.           (ulong)num_elements * elt_size,
  364.           num_elements, elt_size, (ulong)obj);
  365.     return (byte *)obj;
  366. }
  367. private byte *
  368. i_alloc_byte_array_immovable(gs_memory_t *mem, uint num_elements,
  369.   uint elt_size, client_name_t cname)
  370. {    obj_header_t *obj = alloc_obj(imem, (ulong)num_elements * elt_size,
  371.                       &st_bytes, true, cname);
  372.     if_debug6('A', "[a%d|+b.]%s -bytes-*(%lu=%u*%u) = 0x%lx\n",
  373.           imem->space, client_name_string(cname),
  374.           (ulong)num_elements * elt_size,
  375.           num_elements, elt_size, (ulong)obj);
  376.     return (byte *)obj;
  377. }
  378. private void *
  379. i_alloc_struct_array(gs_memory_t *mem, uint num_elements,
  380.   gs_memory_type_ptr_t pstype, client_name_t cname)
  381. {    obj_header_t *obj = alloc_obj(imem,
  382.                 (ulong)num_elements * pstype->ssize,
  383.                 pstype, false, cname);
  384.     if_debug7('A', "[a%d:+<.]%s %s*(%lu=%u*%u) = 0x%lx\n",
  385.           imem->space,
  386.           client_name_string(cname), struct_type_name_string(pstype),
  387.           (ulong)num_elements * pstype->ssize,
  388.           num_elements, pstype->ssize, (ulong)obj);
  389.     return (char *)obj;
  390. }
  391. private void *
  392. i_alloc_struct_array_immovable(gs_memory_t *mem, uint num_elements,
  393.   gs_memory_type_ptr_t pstype, client_name_t cname)
  394. {    obj_header_t *obj = alloc_obj(imem,
  395.                       (ulong)num_elements * pstype->ssize,
  396.                       pstype, true, cname);
  397.     if_debug7('A', "[a%d|+<.]%s %s*(%lu=%u*%u) = 0x%lx\n",
  398.           imem->space,
  399.           client_name_string(cname), struct_type_name_string(pstype),
  400.           (ulong)num_elements * pstype->ssize,
  401.           num_elements, pstype->ssize, (ulong)obj);
  402.     return (char *)obj;
  403. }
  404. private void *
  405. i_resize_object(gs_memory_t *mem, void *obj, uint new_num_elements,
  406.   client_name_t cname)
  407. {    obj_header_t *pp = (obj_header_t *)obj - 1;
  408.     gs_memory_type_ptr_t pstype = pp->o_type;
  409.     ulong old_size = pre_obj_contents_size(pp);
  410.     ulong new_size = (ulong)pstype->ssize * new_num_elements;
  411.     void *new_obj;
  412.  
  413.     if ( (byte *)obj + obj_align_round(old_size) == imem->cc.cbot &&
  414.          imem->cc.ctop - imem->cc.cbot > new_size + obj_align_mod
  415.        )
  416.       { imem->cc.cbot = (byte *)obj + obj_align_round(new_size);
  417.         pp->o_size = new_size;
  418.         if_debug8('A', "[a%d:%c%c ]%s %s(%lu=>%lu) 0x%lx\n",
  419.               imem->space,
  420.               (new_size > old_size ? '>' : '<'),
  421.               (pstype == &st_bytes ? 'b' : '<'),
  422.               client_name_string(cname),
  423.               struct_type_name_string(pstype),
  424.               old_size, new_size, (ulong)obj);
  425.         return obj;
  426.       }
  427.     /* Punt. */
  428.     new_obj = gs_alloc_struct_array(mem, new_num_elements, void,
  429.                     pstype, cname);
  430.     if ( new_obj == 0 )
  431.       return 0;
  432.     memcpy(new_obj, obj, min(old_size, new_size));
  433.     gs_free_object(mem, obj, cname);
  434.     return new_obj;
  435. }
  436. private void
  437. i_free_object(gs_memory_t *mem, void *ptr, client_name_t cname)
  438. {    obj_header_t *pp;
  439.     struct_proc_finalize((*finalize));
  440.     uint size;
  441.  
  442.     if ( ptr == 0 )
  443.       return;
  444.     pp = (obj_header_t *)ptr - 1;
  445. #ifdef DEBUG
  446.     if ( gs_debug_c('?') )
  447.       {    chunk_locator_t cld;
  448.  
  449.         if ( pp->o_type == &st_free )
  450.           {    lprintf2("%s: object 0x%lx already free!\n",
  451.                  client_name_string(cname), (ulong)ptr);
  452.             return;/*gs_abort();*/
  453.           }
  454.         /* Check that this allocator owns the object being freed. */
  455.         cld.memory = imem;
  456.         while ( (cld.cp = cld.memory->clast),
  457.             !chunk_locate_ptr(ptr, &cld)
  458.               )
  459.           { if ( !cld.memory->saved )
  460.               { lprintf3("%s: freeing 0x%lx, not owned by memory 0x%lx!\n",
  461.                  client_name_string(cname), (ulong)ptr,
  462.                  (ulong)mem);
  463.                 return;/*gs_abort();*/
  464.               }
  465.           /****** HACK: we know the saved state is the first ******
  466.            ****** member of an alloc_save_t. ******/
  467.             cld.memory = (gs_ref_memory_t *)cld.memory->saved;
  468.           }
  469.       }
  470. #endif
  471.     size = pre_obj_contents_size(pp);
  472.     finalize = pp->o_type->finalize;
  473.     if ( finalize != 0 )
  474.       {    if_debug3('u', "[u]finalizing %s 0x%lx (%s)\n",
  475.               struct_type_name_string(pp->o_type),
  476.               (ulong)ptr, client_name_string(cname));
  477.         (*finalize)(ptr);
  478.       }
  479.     if ( (byte *)ptr + obj_align_round(size) == imem->cc.cbot )
  480.     {    if_debug4('A', "[a%d:-o ]%s(%u) 0x%lx\n", imem->space,
  481.               client_name_string(cname), size, (ulong)ptr);
  482.         gs_alloc_fill(ptr, gs_alloc_fill_free, size);
  483.         imem->cc.cbot = (byte *)pp;
  484.         return;
  485.     }
  486.     if ( pp->o_large )
  487.     {    /*
  488.          * We gave this object its own chunk.  Free the entire chunk,
  489.          * unless it belongs to an older save level, in which case
  490.          * we mustn't overwrite it.
  491.          */
  492.         chunk_locator_t cl;
  493. #ifdef DEBUG
  494.         { chunk_locator_t cld;
  495.           cld.memory = imem;
  496.           cld.cp = 0;
  497.           if_debug5('a', "[a%d:-o%c]%s(%u) 0x%lx\n", imem->space,
  498.                 (chunk_locate_ptr(ptr, &cld) ? 'L' : '~'),
  499.                 client_name_string(cname), size, (ulong)ptr);
  500.         }
  501. #endif
  502.         cl.memory = imem;
  503.         cl.cp = 0;
  504.         if ( chunk_locate_ptr(ptr, &cl) )
  505.           { alloc_free_chunk(cl.cp, imem);
  506.             return;
  507.           }
  508.         /* Don't overwrite even if gs_alloc_debug is set. */
  509.     }
  510.     if ( size <= max_freelist_size &&
  511.          obj_align_round(size) >= sizeof(obj_header_t *)
  512.        )
  513.       {    /*
  514.          * Put the object on a freelist, unless it belongs to
  515.          * an older save level, in which case we mustn't
  516.          * overwrite it.
  517.          */
  518.         imem->cfreed.memory = imem;
  519.         if ( chunk_locate(ptr, &imem->cfreed) )
  520.           {    obj_header_t **pfl =
  521.               &imem->freelists[(size + obj_align_mask) >>
  522.                        log2_obj_align_mod];
  523.             pp->o_type = &st_free;    /* don't confuse GC */
  524.             gs_alloc_fill(ptr, gs_alloc_fill_free, size);
  525.             *(obj_header_t **)ptr = *pfl;
  526.             *pfl = (obj_header_t *)ptr;
  527.             if_debug4('A', "[a%d:-oF]%s(%u) 0x%lx\n",
  528.                   imem->space, client_name_string(cname),
  529.                   size, (ulong)ptr);
  530.             return;
  531.           }
  532.         /* Don't overwrite even if gs_alloc_debug is set. */
  533.       }
  534.     else
  535.       {    pp->o_type = &st_free;    /* don't confuse GC */
  536.         gs_alloc_fill(ptr, gs_alloc_fill_free, size);
  537.       }
  538.     if_debug4('A', "[a%d:-o#]%s(%u) 0x%lx\n", imem->space,
  539.           client_name_string(cname), size, (ulong)ptr);
  540.     imem->lost.objects += obj_size_round(size);
  541. }
  542. private byte *
  543. i_alloc_string(gs_memory_t *mem, uint nbytes, client_name_t cname)
  544. {    byte *str;
  545. top:    if ( imem->cc.ctop - imem->cc.cbot > nbytes )
  546.     {    if_debug4('A', "[a%d:+> ]%s(%u) = 0x%lx\n", imem->space,
  547.               client_name_string(cname), nbytes,
  548.               (ulong)(imem->cc.ctop - nbytes));
  549.         str = imem->cc.ctop -= nbytes;
  550.         gs_alloc_fill(str, gs_alloc_fill_alloc, nbytes);
  551.         return str;
  552.     }
  553.     if ( nbytes > string_space_quanta(max_uint - sizeof(chunk_head_t)) *
  554.           string_data_quantum
  555.        )
  556.     {    /* Can't represent the size in a uint! */
  557.         return 0;
  558.     }
  559.     if ( nbytes >= imem->large_size )
  560.     {    /* Give it a chunk all its own. */
  561.         return i_alloc_string_immovable(mem, nbytes, cname);
  562.     }
  563.     else
  564.     {    /* Add another chunk. */
  565.         chunk_t *cp =
  566.           alloc_add_chunk(imem, (ulong)imem->chunk_size, true,
  567.                   "chunk");
  568.         if ( cp == 0 )
  569.           return 0;
  570.         alloc_close_chunk(imem);
  571.         imem->pcc = cp;
  572.         imem->cc = *imem->pcc;
  573.         gs_alloc_fill(imem->cc.cbase, gs_alloc_fill_free,
  574.                   imem->cc.climit - imem->cc.cbase);
  575.         goto top;
  576.     }
  577. }
  578. private byte *
  579. i_alloc_string_immovable(gs_memory_t *mem, uint nbytes, client_name_t cname)
  580. {    byte *str;
  581.     /* Give it a chunk all its own. */
  582.     uint asize = string_chunk_space(nbytes) + sizeof(chunk_head_t);
  583.     chunk_t *cp = alloc_add_chunk(imem, (ulong)asize, true,
  584.                       "large string chunk");
  585.     if ( cp == 0 )
  586.       return 0;
  587.     str = cp->ctop = cp->climit - nbytes;
  588.     if_debug4('a', "[a%d|+>L]%s(%u) = 0x%lx\n", imem->space,
  589.           client_name_string(cname), nbytes, (ulong)str);
  590.     gs_alloc_fill(str, gs_alloc_fill_alloc, nbytes);
  591.     return str;
  592. }
  593. private byte *
  594. i_resize_string(gs_memory_t *mem, byte *data, uint old_num, uint new_num,
  595.   client_name_t cname)
  596. {    byte *ptr;
  597.     if ( data == imem->cc.ctop &&
  598.            (new_num < old_num ||
  599.         imem->cc.ctop - imem->cc.cbot > new_num - old_num)
  600.        )
  601.     {    /* Resize in place. */
  602.         ptr = data + old_num - new_num;
  603.         if_debug6('A', "[a%d:%c> ]%s(%u->%u) 0x%lx\n",
  604.               imem->space, (new_num > old_num ? '>' : '<'),
  605.               client_name_string(cname), old_num, new_num,
  606.               (ulong)ptr);
  607.         imem->cc.ctop = ptr;
  608.         memmove(ptr, data, min(old_num, new_num));
  609. #ifdef DEBUG
  610.         if ( new_num > old_num )
  611.           gs_alloc_fill(ptr + old_num, gs_alloc_fill_alloc,
  612.                 new_num - old_num);
  613.         else
  614.           gs_alloc_fill(data, gs_alloc_fill_free, old_num - new_num);
  615. #endif
  616.     }
  617.     else
  618.     {    /* Punt. */
  619.         ptr = gs_alloc_string(mem, new_num, cname);
  620.         if ( ptr == 0 )
  621.           return 0;
  622.         memcpy(ptr, data, min(old_num, new_num));
  623.         gs_free_string(mem, data, old_num, cname);
  624.     }
  625.     return ptr;
  626. }
  627. private void
  628. i_free_string(gs_memory_t *mem, byte *data, uint nbytes,
  629.   client_name_t cname)
  630. {    if ( data == imem->cc.ctop )
  631.     {    if_debug4('A', "[a%d:-> ]%s(%u) 0x%lx\n", imem->space,
  632.               client_name_string(cname), nbytes, (ulong)data);
  633.         imem->cc.ctop += nbytes;
  634.     }
  635.     else
  636.     {    if_debug4('A', "[a%d:->#]%s(%u) 0x%lx\n", imem->space,
  637.               client_name_string(cname), nbytes, (ulong)data);
  638.         imem->lost.strings += nbytes;
  639.     }
  640.     gs_alloc_fill(data, gs_alloc_fill_free, nbytes);
  641. }
  642. private void
  643. i_status(gs_memory_t *mem, gs_memory_status_t *pstat)
  644. {    ulong unused = imem->lost.refs + imem->lost.strings;
  645.     ulong inner = 0;
  646.  
  647.     alloc_close_chunk(imem);
  648.     /* Add up unallocated space within each chunk. */
  649.     /* Also keep track of space allocated to inner chunks, */
  650.     /* which are included in previous_status.allocated. */
  651.       {    const chunk_t *cp = imem->cfirst;
  652.         while ( cp != 0 )
  653.           {    unused += cp->ctop - cp->cbot;
  654.             if ( cp->outer )
  655.               inner += cp->cend - (byte *)cp->chead;
  656.             cp = cp->cnext;
  657.           }
  658.       }
  659.     unused += compute_free_objects(imem);
  660.     pstat->used = imem->allocated + inner - unused +
  661.       imem->previous_status.used;
  662.     pstat->allocated = imem->allocated +
  663.       imem->previous_status.allocated;
  664. }
  665. private void
  666. i_enable_free(gs_memory_t *mem, bool enable)
  667. {    if ( enable )
  668.       mem->procs.free_object = i_free_object,
  669.       mem->procs.free_string = i_free_string;
  670.     else
  671.       mem->procs.free_object = gs_ignore_free_object,
  672.       mem->procs.free_string = gs_ignore_free_string;
  673. }
  674.  
  675. /* ------ Internal procedures ------ */
  676.  
  677. /* Compute the amount of free object space by scanning free lists. */
  678. private ulong
  679. compute_free_objects(gs_ref_memory_t *mem)
  680. {    ulong unused = mem->lost.objects;
  681.     int i;
  682.  
  683.     /* Add up space on free lists. */
  684.     for ( i = 0; i < num_freelists; i++ )
  685.       {    uint free_size =
  686.           (i << log2_obj_align_mod) + sizeof(obj_header_t);
  687.         const obj_header_t *pfree;
  688.  
  689.         for ( pfree = mem->freelists[i]; pfree != 0;
  690.               pfree = *(const obj_header_t * const *)pfree
  691.             )
  692.           unused += free_size;
  693.       }
  694.     return unused;
  695. }
  696.  
  697. /* Allocate an object.  This handles all but the fastest, simplest case. */
  698. private obj_header_t *
  699. alloc_obj(gs_ref_memory_t *mem, ulong lsize, gs_memory_type_ptr_t pstype,
  700.   bool immovable, client_name_t cname)
  701. {    obj_header_t *ptr;
  702.     if ( lsize >= mem->large_size || immovable )
  703.     {    ulong asize =
  704.           ((lsize + obj_align_mask) & -obj_align_mod) +
  705.             sizeof(obj_header_t);
  706.         /* Give it a chunk all its own. */
  707.         chunk_t *cp =
  708.           alloc_add_chunk(mem, asize + sizeof(chunk_head_t), false,
  709.                   "large object chunk");
  710.         if ( cp == 0 )
  711.             return 0;
  712.         ptr = (obj_header_t *)cp->cbot;
  713.         cp->cbot += asize;
  714.         ptr->o_large = 1;
  715.         pre_obj_set_large_size(ptr, lsize);
  716.     }
  717.     else
  718.     {    uint asize = obj_size_round((uint)lsize);
  719.         while ( mem->cc.ctop -
  720.              (byte *)(ptr = (obj_header_t *)mem->cc.cbot)
  721.               <= asize + sizeof(obj_header_t) )
  722.         {    /* Add another chunk. */
  723.             chunk_t *cp =
  724.               alloc_add_chunk(mem, (ulong)mem->chunk_size,
  725.                       true, "chunk");
  726.             if ( cp == 0 )
  727.                 return 0;
  728.             alloc_close_chunk(mem);
  729.             mem->pcc = cp;
  730.             mem->cc = *mem->pcc;
  731.             gs_alloc_fill(mem->cc.cbase, gs_alloc_fill_free,
  732.                       mem->cc.climit - mem->cc.cbase);
  733.         }
  734.         mem->cc.cbot = (byte *)ptr + asize;
  735.         ptr->o_large = 0;
  736.         ptr->o_size = (uint)lsize;
  737.     }
  738.     ptr->o_type = pstype;
  739.     ptr++;
  740.         gs_alloc_fill(ptr, gs_alloc_fill_alloc, lsize);
  741.     return ptr;
  742. }
  743.  
  744. /* ================ Roots ================ */
  745.  
  746. /* Register a root. */
  747. private void
  748. i_register_root(gs_memory_t *mem, gs_gc_root_t *rp, gs_ptr_type_t ptype,
  749.   void **up, client_name_t cname)
  750. {    if_debug3('8', "[8]register root(%s) 0x%lx -> 0x%lx\n",
  751.          client_name_string(cname), (ulong)rp, (ulong)up);
  752.     rp->ptype = ptype, rp->p = up;
  753.     rp->next = imem->roots, imem->roots = rp;
  754. }
  755.  
  756. /* Unregister a root. */
  757. private void
  758. i_unregister_root(gs_memory_t *mem, gs_gc_root_t *rp, client_name_t cname)
  759. {    gs_gc_root_t **rpp = &imem->roots;
  760.     if_debug2('8', "[8]unregister root(%s) 0x%lx\n",
  761.         client_name_string(cname), (ulong)rp);
  762.     while ( *rpp != rp ) rpp = &(*rpp)->next;
  763.     *rpp = (*rpp)->next;
  764. }
  765.  
  766. /* ================ Chunks ================ */
  767.  
  768. public_st_chunk();
  769.  
  770. /* Insert a chunk in the chain.  This is exported for the GC and for */
  771. /* the forget_save operation. */
  772. void
  773. alloc_link_chunk(chunk_t *cp, gs_ref_memory_t *mem)
  774. {    byte *cdata = cp->cbase;
  775.     chunk_t *icp;
  776.     chunk_t *prev;
  777.     for ( icp = mem->cfirst; icp != 0 && ptr_ge(cdata, icp->ctop);
  778.           icp = icp->cnext
  779.         )
  780.         ;
  781.     cp->cnext = icp;
  782.     if ( icp == 0 )            /* add at end of chain */
  783.     {    prev = imem->clast;
  784.         imem->clast = cp;
  785.     }
  786.     else                /* insert before icp */
  787.     {    prev = icp->cprev;
  788.         icp->cprev = cp;
  789.     }
  790.     cp->cprev = prev;
  791.     if ( prev == 0 )
  792.         imem->cfirst = cp;
  793.     else
  794.         prev->cnext = cp;
  795.     if ( imem->pcc != 0 )
  796.     {    imem->cc.cnext = imem->pcc->cnext;
  797.         imem->cc.cprev = imem->pcc->cprev;
  798.     }
  799. }
  800.  
  801. /* Allocate a chunk.  If we would exceed MaxLocalVM (if relevant), */
  802. /* or if we would exceed the VMThreshold and psignal is NULL, */
  803. /* return 0; if we would exceed the VMThreshold but psignal is valid, */
  804. /* just set the signal and return successfully. */
  805. private chunk_t *
  806. alloc_add_chunk(gs_ref_memory_t *mem, ulong csize, bool has_strings,
  807.   client_name_t cname)
  808. {    gs_memory_t *parent = mem->parent;
  809.     chunk_t *cp = gs_alloc_struct_immovable(parent, chunk_t, &st_chunk,
  810.                         cname);
  811.     byte *cdata;
  812.     /* If csize is larger than max_uint, */
  813.     /* we have to fake it using gs_alloc_byte_array. */
  814.     ulong elt_size = csize;
  815.     uint num_elts = 1;
  816.     if ( (ulong)(mem->allocated + mem->inherited) >= mem->limit )
  817.       {    mem->gc_status.requested += csize;
  818.         if ( mem->limit >= mem->gc_status.max_vm ||
  819.              mem->gc_status.psignal == 0
  820.            )
  821.             return 0;
  822.         if_debug4('0', "[0]signaling space=%d, allocated=%ld, limit=%ld, requested=%ld\n",
  823.               mem->space, (long)mem->allocated,
  824.               (long)mem->limit, (long)mem->gc_status.requested);
  825.         *mem->gc_status.psignal = mem->gc_status.signal_value;
  826.       }
  827.     while ( (uint)elt_size != elt_size )
  828.       elt_size = (elt_size + 1) >> 1,
  829.       num_elts <<= 1;
  830.     cdata = gs_alloc_byte_array_immovable(parent, num_elts, elt_size,
  831.                           cname);
  832.     if ( cp == 0 || cdata == 0 )
  833.     {    gs_free_object(parent, cdata, cname);
  834.         gs_free_object(parent, cp, cname);
  835.         mem->gc_status.requested = csize;
  836.         return 0;
  837.     }
  838.     alloc_init_chunk(cp, cdata, cdata + csize, has_strings, (chunk_t *)0);
  839.     alloc_link_chunk(cp, mem);
  840.     mem->allocated +=
  841.       gs_object_size(parent, cdata) + gs_object_size(parent, cp);
  842.     return cp;
  843. }
  844.  
  845. /* Initialize the pointers in a chunk.  This is exported for save/restore. */
  846. /* The bottom pointer must be aligned, but the top pointer need not */
  847. /* be aligned. */
  848. void
  849. alloc_init_chunk(chunk_t *cp, byte *bot, byte *top, bool has_strings,
  850.   chunk_t *outer)
  851. {    byte *cdata = bot;
  852.     if ( outer != 0 )
  853.       outer->inner_count++;
  854.     cp->chead = (chunk_head_t *)cdata;
  855.     cdata += sizeof(chunk_head_t);
  856.     cp->cbot = cp->cbase = cdata;
  857.     cp->cend = top;
  858.     cp->rcur = 0;
  859.     cp->rtop = 0;
  860.     cp->outer = outer;
  861.     cp->inner_count = 0;
  862.     cp->has_refs = false;
  863.     cp->sbase = cdata;
  864.     if ( has_strings && top - cdata >= string_space_quantum + sizeof(long) - 1)
  865.     {    /*
  866.          * We allocate a large enough string marking and reloc table
  867.          * to cover the entire chunk.
  868.          */
  869.         uint nquanta = string_space_quanta(top - cdata);
  870.         cp->climit = cdata + nquanta * string_data_quantum;
  871.         cp->smark = cp->climit;
  872.         cp->smark_size = string_quanta_mark_size(nquanta);
  873.         cp->sreloc =
  874.           (string_reloc_offset *)(cp->smark + cp->smark_size);
  875.         cp->sfree1 = (ushort *)cp->sreloc;
  876.     }
  877.     else
  878.     {    /* No strings, don't need the string GC tables. */
  879.         cp->climit = cp->cend;
  880.         cp->sfree1 = 0;
  881.         cp->smark = 0;
  882.         cp->smark_size = 0;
  883.         cp->sreloc = 0;
  884.     }
  885.     cp->ctop = cp->climit;
  886.     alloc_init_free_strings(cp);
  887. }
  888.  
  889. /* Initialize the string freelists in a chunk. */
  890. void
  891. alloc_init_free_strings(chunk_t *cp)
  892. {    if ( cp->sfree1 )
  893.       memset(cp->sfree1, 0,
  894.          ((cp->climit - csbase(cp) + 255) >> 8) *
  895.            sizeof(*cp->sfree1));
  896.     cp->sfree = 0;
  897. }
  898.  
  899. /* Close up the current chunk. */
  900. /* This is exported for save/restore and the GC. */
  901. void
  902. alloc_close_chunk(gs_ref_memory_t *mem)
  903. {    if ( mem->pcc != 0 )
  904.     {    *mem->pcc = mem->cc;
  905. #ifdef DEBUG
  906.         if ( gs_debug_c('a') )
  907.           {    dprintf1("[a%d]", mem->space);
  908.             dprintf_chunk("closing chunk", mem->pcc);
  909.           }
  910. #endif
  911.     }
  912. }
  913.  
  914. /* Reopen the current chunk after a GC or restore. */
  915. void
  916. alloc_open_chunk(gs_ref_memory_t *mem)
  917. {    if ( mem->pcc != 0 )
  918.     {    mem->cc = *mem->pcc;
  919. #ifdef DEBUG
  920.         if ( gs_debug_c('a') )
  921.           {    dprintf1("[a%d]", mem->space);
  922.             dprintf_chunk("opening chunk", mem->pcc);
  923.           }
  924. #endif
  925.     }
  926. }
  927.  
  928. /* Remove a chunk from the chain.  This is exported for the GC. */
  929. void
  930. alloc_unlink_chunk(chunk_t *cp, gs_ref_memory_t *mem)
  931. {
  932. #ifdef DEBUG
  933.     if ( gs_alloc_debug )
  934.       {    /* Check to make sure this chunk belongs to this allocator. */
  935.         const chunk_t *ap = mem->cfirst;
  936.         while ( ap != 0 && ap != cp )
  937.           ap = ap->cnext;
  938.         if ( ap != cp )
  939.           {    lprintf2("unlink_chunk 0x%lx not owned by memory 0x%lx!\n",
  940.                  (ulong)cp, (ulong)mem);
  941.             return;/*gs_abort();*/
  942.           }
  943.       }
  944. #endif
  945.     if ( cp->cprev == 0 )
  946.       mem->cfirst = cp->cnext;
  947.     else
  948.       cp->cprev->cnext = cp->cnext;
  949.     if ( cp->cnext == 0 )
  950.       mem->clast = cp->cprev;
  951.     else
  952.       cp->cnext->cprev = cp->cprev;
  953.     if ( mem->pcc != 0 )
  954.       {    mem->cc.cnext = mem->pcc->cnext;
  955.         mem->cc.cprev = mem->pcc->cprev;
  956.         if ( mem->pcc == cp )
  957.           {    mem->pcc = 0;
  958.             mem->cc.cbot = mem->cc.ctop = 0;
  959.           }
  960.       }
  961. }
  962.  
  963. /* Free a chunk.  This is exported for save/restore and for the GC. */
  964. void
  965. alloc_free_chunk(chunk_t *cp, gs_ref_memory_t *mem)
  966. {    gs_memory_t *parent = mem->parent;
  967.     alloc_unlink_chunk(cp, mem);
  968.     if ( mem->cfreed.cp == cp )
  969.       mem->cfreed.cp = 0;
  970.     if ( cp->outer == 0 )
  971.       {    byte *cdata = (byte *)cp->chead;
  972.         mem->allocated -= gs_object_size(parent, cdata);
  973.         gs_free_object(parent, cdata, "alloc_free_chunk(data)");
  974.       }
  975.     else
  976.       cp->outer->inner_count--;
  977.     mem->allocated -= gs_object_size(parent, cp);
  978.     gs_free_object(parent, cp, "alloc_free_chunk(chunk struct)");
  979. }
  980.  
  981. /* Find the chunk for a pointer. */
  982. /* Note that this only searches the current save level. */
  983. /* Since a given save level can't contain both a chunk and an inner chunk */
  984. /* of that chunk, we can stop when is_within_chunk succeeds, and just test */
  985. /* is_in_inner_chunk then. */
  986. bool
  987. chunk_locate_ptr(const void *vptr, chunk_locator_t *clp)
  988. {    register chunk_t *cp = clp->cp;
  989.     if ( cp == 0 )
  990.     {    cp = clp->memory->cfirst;
  991.         if ( cp == 0 )
  992.             return false;
  993.     }
  994. #define ptr (const byte *)vptr
  995.     if ( ptr_lt(ptr, cp->cbase) )
  996.     {    do
  997.         {    cp = cp->cprev;
  998.             if ( cp == 0 )
  999.                 return false;
  1000.         }
  1001.         while ( ptr_lt(ptr, cp->cbase) );
  1002.         if ( ptr_ge(ptr, cp->cend) )
  1003.           return false;
  1004.     }
  1005.     else
  1006.     {    while ( ptr_ge(ptr, cp->cend) )
  1007.         {    cp = cp->cnext;
  1008.             if ( cp == 0 )
  1009.                 return false;
  1010.         }
  1011.         if ( ptr_lt(ptr, cp->cbase) )
  1012.           return false;
  1013.     }
  1014.     clp->cp = cp;
  1015.     return !ptr_is_in_inner_chunk(ptr, cp);
  1016. #undef ptr
  1017. }
  1018.  
  1019. /* ------ Debugging printout ------ */
  1020.  
  1021. /*
  1022.  * All of this code should be in a separate file, but we added it just
  1023.  * before a release, and it would have been too disruptive to add a new
  1024.  * file at this point.
  1025.  */
  1026.  
  1027. #ifdef DEBUG
  1028.  
  1029. #include "string_.h"
  1030.  
  1031. /*
  1032.  * Define the options for a memory dump.  These may be or'ed together.
  1033.  */
  1034. typedef enum {
  1035.   dump_do_default = 0,        /* pro forma */
  1036.   dump_do_strings = 1,
  1037.   dump_do_type_addresses = 2,
  1038.   dump_do_no_types = 4,
  1039.   dump_do_pointers = 8,
  1040.   dump_do_pointed_strings = 16,    /* only if do_pointers also set */
  1041.   dump_do_contents = 32,
  1042.   dump_do_marks = 64
  1043. } dump_options_t;
  1044.  
  1045. /*
  1046.  * Define all the parameters controlling what gets dumped.
  1047.  */
  1048. typedef struct dump_control_s {
  1049.   dump_options_t options;
  1050.   const byte *bottom;
  1051.   const byte *top;
  1052. } dump_control_t;
  1053. #define obj_in_control_region(obot, otop, pdc)\
  1054.   ( ((pdc)->bottom == NULL || (const byte *)(otop) > (pdc)->bottom) &&\
  1055.     ((pdc)->top == NULL || (const byte *)(obot) < (pdc)->top) )
  1056. const dump_control_t dump_control_default = {
  1057.   dump_do_default, NULL, NULL
  1058. };
  1059. const dump_control_t dump_control_all = {
  1060.   dump_do_strings | dump_do_type_addresses | dump_do_pointers |
  1061.   dump_do_pointed_strings | dump_do_contents, NULL, NULL
  1062. };
  1063.  
  1064. /*
  1065.  * Internal procedure to dump a block of memory, in hex and optionally
  1066.  * also as characters.
  1067.  */
  1068. private void
  1069. debug_indent(int indent)
  1070. {    int i;
  1071.     for ( i = indent; i > 0; --i )
  1072.       dputc(' ');
  1073. }
  1074. private void
  1075. debug_dump_contents(const byte *bot, const byte *top, int indent,
  1076.   bool as_chars)
  1077. {    const byte *block;
  1078. #define block_size 16
  1079.  
  1080.     if ( bot >= top )
  1081.       return;
  1082.     for ( block = bot - ((bot - (byte *)0) & (block_size - 1));
  1083.           block < top; block += block_size
  1084.         ) {
  1085.       int i;
  1086.       char label[12];
  1087.  
  1088.       /* Check for repeated blocks. */
  1089.       if ( block >= bot + block_size &&
  1090.            block <= top - (block_size * 2) &&
  1091.            !memcmp(block, block - block_size, block_size) &&
  1092.            !memcmp(block, block + block_size, block_size)
  1093.          ) {
  1094.         if ( block < bot + block_size * 2 ||
  1095.          memcmp(block, block - block_size * 2, block_size)
  1096.            ) {
  1097.           debug_indent(indent);
  1098.           dputs("  ...\n");
  1099.         }
  1100.         continue;
  1101.       }
  1102.       sprintf(label, "0x%lx:", (ulong)block);
  1103.       debug_indent(indent);
  1104.       dputs(label);
  1105.       for ( i = 0; i < block_size; ++i ) {
  1106.         const char *sepr = ((i & 3) == 0 && i != 0 ? "  " : " ");
  1107.  
  1108.         dputs(sepr);
  1109.         if ( block + i >= bot && block + i < top )
  1110.           dprintf1("%02x", block[i]);
  1111.         else
  1112.           dputs("  ");
  1113.       }
  1114.       dputc('\n');
  1115.       if ( as_chars ) {
  1116.         debug_indent(indent + strlen(label));
  1117.         for ( i = 0; i < block_size; ++i ) {
  1118.           byte ch;
  1119.           if ( (i & 3) == 0 && i != 0 )
  1120.         dputc(' ');
  1121.           if ( block + i >= bot && block + i < top &&
  1122.            (ch = block[i]) >= 32 && ch <= 126
  1123.          )
  1124.         dprintf1("  %c", ch);
  1125.           else
  1126.         dputs("   ");
  1127.         }
  1128.         dputc('\n');
  1129.       }
  1130.     }
  1131. #undef block_size
  1132. }
  1133.  
  1134. /* Print one object with the given options. */
  1135. /* Relevant options: type_addresses, no_types, pointers, pointed_strings, */
  1136. /* contents. */
  1137. void
  1138. debug_print_object(const void *obj, const dump_control_t *control)
  1139. {    const obj_header_t *pre = ((const obj_header_t *)obj) - 1;
  1140.     ulong size = pre_obj_contents_size(pre);
  1141.     const gs_memory_struct_type_t *type = pre->o_type;
  1142.     dump_options_t options = control->options;
  1143.  
  1144.     dprintf3("  pre=0x%lx(obj=0x%lx) size=%lu", (ulong)pre, (ulong)obj,
  1145.          size);
  1146.     switch ( options & (dump_do_type_addresses | dump_do_no_types) )
  1147.       {
  1148.       case dump_do_type_addresses + dump_do_no_types: /* addresses only */
  1149.         dprintf1(" type=0x%lx", (ulong)type);
  1150.         break;
  1151.       case dump_do_type_addresses: /* addresses & names */
  1152.         dprintf2(" type=%s(0x%lx)", struct_type_name_string(type),
  1153.              (ulong)type);
  1154.         break;
  1155.       case 0:        /* names only */
  1156.         dprintf1(" type=%s", struct_type_name_string(type));
  1157.       case dump_do_no_types: /* nothing */
  1158.         ;
  1159.       }
  1160.     if ( options & dump_do_marks ) {
  1161.       if ( pre->o_large )
  1162.         dprintf1(" lmark=%d", pre->o_lmark);
  1163.       else
  1164.         dprintf2(" smark/back=%u (0x%x)", pre->o_smark, pre->o_smark);
  1165.     }
  1166.     dputc('\n');
  1167.     if ( type == &st_free )
  1168.       return;
  1169.     if ( options & dump_do_pointers ) {
  1170.       struct_proc_enum_ptrs((*proc)) = type->enum_ptrs;
  1171.       uint index = 0;
  1172.       const void *ptr;
  1173.       gs_ptr_type_t ptype;
  1174.  
  1175.       /*
  1176.        * NOTE: the following cast should be unnecessary, but that
  1177.        * will require adding 'const' to the first prototype argument
  1178.        * of struct_proc_enum_ptrs.
  1179.        */
  1180.       if ( proc != 0 )
  1181.         for ( ; (ptype = (*proc)((obj_header_t *)pre + 1, size, index, &ptr)) != 0;
  1182.           ++index
  1183.         ) {
  1184.           dprintf1("    ptr %u: ", index);
  1185.           if ( ptype == ptr_string_type || ptype == ptr_const_string_type ) {
  1186.         const gs_const_string *str = (const gs_const_string *)ptr;
  1187.  
  1188.         dprintf2("0x%lx(%u)", (ulong)str->data, str->size);
  1189.         if ( options & dump_do_pointed_strings ) {
  1190.           dputs(" =>\n");
  1191.           debug_dump_contents(str->data, str->data + str->size, 6,
  1192.                       true);
  1193.         } else {
  1194.           dputc('\n');
  1195.         }
  1196.           } else {
  1197.         dprintf1((ptr_between(ptr, obj, (const byte *)obj + size) ?
  1198.               "(0x%lx)\n" : "0x%lx\n"), (ulong)ptr);
  1199.           }
  1200.         }
  1201.     }
  1202.     if ( options & dump_do_contents ) {
  1203.       debug_dump_contents((const byte *)obj, (const byte *)obj + size,
  1204.                   0, false);
  1205.     }
  1206. }
  1207.  
  1208. /* Print the contents of a chunk with the given options. */
  1209. /* Relevant options: all. */
  1210. void
  1211. debug_dump_chunk(const chunk_t *cp, const dump_control_t *control)
  1212. {    dprintf1("chunk at 0x%lx:\n", (ulong)cp);
  1213.     dprintf3("   chead=0x%lx  cbase=0x%lx sbase=0x%lx\n",
  1214.          (ulong)cp->chead, (ulong)cp->cbase, (ulong)cp->sbase);
  1215.     dprintf3("    rcur=0x%lx   rtop=0x%lx  cbot=0x%lx\n",
  1216.          (ulong)cp->rcur, (ulong)cp->rtop, (ulong)cp->cbot);
  1217.     dprintf4("    ctop=0x%lx climit=0x%lx smark=0x%lx, size=%u\n",
  1218.          (ulong)cp->ctop, (ulong)cp->climit, (ulong)cp->smark,
  1219.          cp->smark_size);
  1220.     dprintf2("  sreloc=0x%lx   cend=0x%lx\n",
  1221.          (ulong)cp->sreloc, (ulong)cp->cend);
  1222.     dprintf5("cprev=0x%lx cnext=0x%lx outer=0x%lx inner_count=%u has_refs=%s\n",
  1223.          (ulong)cp->cprev, (ulong)cp->cnext, (ulong)cp->outer,
  1224.          cp->inner_count, (cp->has_refs? "true" : "false"));
  1225.  
  1226.     dprintf2("  sfree1=0x%lx   sfree=0x%x\n",
  1227.          (ulong)cp->sfree1, cp->sfree);
  1228.     if ( control->options & dump_do_strings ) {
  1229.       debug_dump_contents((control->bottom == 0 ? cp->ctop :
  1230.                    max(control->bottom, cp->ctop)),
  1231.                   (control->top == 0 ? cp->climit :
  1232.                    min(control->top, cp->climit)),
  1233.                   0, true);
  1234.     }
  1235.     SCAN_CHUNK_OBJECTS(cp)
  1236.       DO_ALL
  1237.         if ( obj_in_control_region(pre + 1,
  1238.                        (const byte *)(pre + 1) + size,
  1239.                        control)
  1240.            )
  1241.           debug_print_object(pre + 1, control);
  1242. /* Temporarily redefine gs_exit so a chunk parsing error */
  1243. /* won't actually exit. */
  1244. #define gs_exit(n) DO_NOTHING
  1245.     END_OBJECTS_SCAN
  1246. #undef gs_exit
  1247. }
  1248. void debug_print_chunk(const chunk_t *cp)
  1249. {    dump_control_t control;
  1250.  
  1251.     control = dump_control_default;
  1252.     debug_dump_chunk(cp, &control);
  1253. }
  1254.  
  1255. /* Print the contents of all chunks managed by an allocator. */
  1256. /* Relevant options: all. */
  1257. void
  1258. debug_dump_memory(const gs_ref_memory_t *mem, const dump_control_t *control)
  1259. {    const chunk_t *mcp;
  1260.  
  1261.     for ( mcp = mem->cfirst; mcp != 0; mcp = mcp->cnext ) {
  1262.       const chunk_t *cp = (mcp == mem->pcc ? &mem->cc : mcp);
  1263.  
  1264.       if ( obj_in_control_region(cp->cbase, cp->cend, control) )
  1265.         debug_dump_chunk(cp, control);
  1266.     }
  1267. }
  1268.  
  1269. #endif                    /* DEBUG */
  1270.